package sf.database;

import sf.common.log.LogUtil;
import sf.core.DBObject;
import sf.database.dao.DBClient;
import sf.database.support.EntityScanner;
import sf.database.template.TemplateRender;
import sf.database.template.TemplateType;
import sf.database.util.OrmValueUtils.BeanValueType;
import sf.tools.StringUtils;

import javax.sql.DataSource;
import java.util.Map;

public class OrmConfig {
    protected static OrmConfig instance = new OrmConfig();

    public static OrmConfig getInstance() {
        return instance;
    }

    public OrmConfig() {

    }

    /**
     * 多数据源。分库分表时可以使用。 在Spring配置时，可以使用这样的格式来配置
     *
     * <pre>
     * <code>
     * &lt;property name="dataSources"&gt;
     * 	&lt;map&gt;
     * 	 &lt;entry key="dsname1" value-ref="ds1" /&gt;
     * 	 &lt;entry key="dsname2" value-ref="ds2" /&gt;
     * 	&lt;/map&gt;
     * &lt;/property&gt;
     * </code>
     * </pre>
     */
    protected Map<String, DataSource> dataSources;

    /**
     * 单数据源。
     */
    protected DataSource dataSource;

    /**
     * 多数据源时的缺省数据源名称
     */
    protected String defaultDatasource;


    /**
     * 指定扫描若干包,配置示例如下—— <code><pre>
     * &lt;list&gt;
     *  &lt;value&gt;org.sf.test&lt;/value&gt;
     *  &lt;value&gt;org.sf.entity&lt;/value&gt;
     * &lt;/list&gt;
     * </pre></code>
     */
    protected String[] packagesToScan;

    /**
     * 对配置了包扫描的路径进行增强检查，方便单元测试
     */
    protected boolean enhanceScanPackages = true;

    /**
     * 扫描已知的若干注解实体类，配置示例如下—— <code><pre>
     * &lt;list&gt;
     *  &lt;value&gt;org.sf.testp.jta.Product&lt;/value&gt;
     *  &lt;value&gt;org.sf.testp.jta.Users&lt;/value&gt;
     * &lt;/list&gt;
     * </pre></code>
     */
    protected String[] annotatedClasses;


    /**
     * 扫描到实体后，如果数据库中不存在，是否建表 <br>
     * 默认开启
     */
    protected boolean createTable = true;

    /**
     * 扫描到实体后，如果数据库中存在对应表，是否修改表 <br>
     * 默认开启
     */
    protected boolean alterTable = true;

    /**
     * 扫描到实体后，如果准备修改表，如果数据库中的列更多，是否允许删除列 <br>
     * 默认关闭
     */
    protected boolean allowDropColumn;


    //以下为新增.
    /**
     * 连接
     */
    protected DBClient dbClient;

    /**
     * 批处理数量
     */
    protected int batchSize = 100;
    /**
     * sql日志开关
     */
    protected boolean showSql = true;
    /**
     * 是否打印方法调用,在showSql=true的情况下才有用
     */
    protected boolean showCallChain = false;
    /**
     * 是否使用系统打印日志
     */
    protected boolean useSystemPrint = true;

    /**
     * bean转换方式
     * 快速设置bean方法 使用unsafe实现
     */
    protected BeanValueType beanValueType = BeanValueType.method;

    /**
     * 是否使用tail获取额外属性.
     */
    protected boolean useTail = false;


    /**
     * 是否开启sql模板调试模式
     */
    protected boolean sqlTemplateDebug = false;
    /**
     * sql模板位置
     */
    protected String sqlTemplatePath = "classpath*:sql/**/*.sql";
    /**
     * 是否开启流式迭代(默认关闭)
     */
    public boolean openStreamIterator = false;

    /**
     * 使用的sql模板类型
     */
    protected TemplateType sqlTemplateType = TemplateType.enjoy;

    /**
     * 是否需要检查类增强
     */
    protected boolean checkEnhancement = true;

    public void init() {
        init(this);
    }

    /**
     * 扫描类允许传入
     * @param config
     */
    public void init(OrmConfig config) {
        if (StringUtils.isNotBlank(config.getSqlTemplatePath())) {
            TemplateRender.path = config.getSqlTemplatePath().split(",");
        }
        if (config.getPackagesToScan() != null || config.getAnnotatedClasses() != null) {
            EntityScanner qe = new EntityScanner();
//            if (transactionMode == TransactionMode.JTA) {
            // JTA事务下，DDL语句必须在已启动后立刻就做，迟了就被套进JTA的事务中，出错。
            qe.setCheckSequence(false);
//            }
            qe.setImplClasses(DBObject.class);
            qe.setAllowDropColumn(config.isAllowDropColumn());
            qe.setAlterTable(config.isAlterTable());
            qe.setCreateTable(config.isCreateTable());
            qe.setDbClient(config.getDbClient());
            if (config.getAnnotatedClasses() != null)
                qe.registeEntity(config.getAnnotatedClasses());
            if (config.getPackagesToScan() != null) {
                String joined = StringUtils.join(config.getPackagesToScan(), ",");
                qe.setPackageNames(joined);
                LogUtil.info("Starting scan orm entity from package: {}", joined);
                qe.doScan();
            }
        }
    }


    public Map<String, DataSource> getDataSources() {
        return dataSources;
    }

    public void setDataSources(Map<String, DataSource> dataSources) {
        this.dataSources = dataSources;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public String getDefaultDatasource() {
        return defaultDatasource;
    }

    public void setDefaultDatasource(String defaultDatasource) {
        this.defaultDatasource = defaultDatasource;
    }

    public String[] getPackagesToScan() {
        return packagesToScan;
    }

    public void setPackagesToScan(String[] packagesToScan) {
        this.packagesToScan = packagesToScan;
    }

    public boolean isEnhanceScanPackages() {
        return enhanceScanPackages;
    }

    public void setEnhanceScanPackages(boolean enhanceScanPackages) {
        this.enhanceScanPackages = enhanceScanPackages;
    }

    public String[] getAnnotatedClasses() {
        return annotatedClasses;
    }

    public void setAnnotatedClasses(String[] annotatedClasses) {
        this.annotatedClasses = annotatedClasses;
    }

    public boolean isCreateTable() {
        return createTable;
    }

    public void setCreateTable(boolean createTable) {
        this.createTable = createTable;
    }

    public boolean isAlterTable() {
        return alterTable;
    }

    public void setAlterTable(boolean alterTable) {
        this.alterTable = alterTable;
    }

    public boolean isAllowDropColumn() {
        return allowDropColumn;
    }

    public void setAllowDropColumn(boolean allowDropColumn) {
        this.allowDropColumn = allowDropColumn;
    }

    public DBClient getDbClient() {
        return dbClient;
    }

    public void setDbClient(DBClient dbClient) {
        this.dbClient = dbClient;
    }

    public boolean isShowSql() {
        return showSql;
    }

    public void setShowSql(boolean showSql) {
        this.showSql = showSql;
    }

    public boolean isShowCallChain() {
        return showCallChain;
    }

    public void setShowCallChain(boolean showCallChain) {
        this.showCallChain = showCallChain;
    }

    public BeanValueType getBeanValueType() {
        return beanValueType;
    }

    public void setBeanValueType(BeanValueType beanValueType) {
        this.beanValueType = beanValueType;
    }

    public boolean isUseTail() {
        return useTail;
    }

    public void setUseTail(boolean useTail) {
        this.useTail = useTail;
    }

    public String getSqlTemplatePath() {
        return sqlTemplatePath;
    }

    public void setSqlTemplatePath(String sqlTemplatePath) {
        this.sqlTemplatePath = sqlTemplatePath;
    }

    public boolean isSqlTemplateDebug() {
        return sqlTemplateDebug;
    }

    public void setSqlTemplateDebug(boolean sqlTemplateDebug) {
        this.sqlTemplateDebug = sqlTemplateDebug;
    }

    public int getBatchSize() {
        return batchSize;
    }

    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    public boolean isOpenStreamIterator() {
        return openStreamIterator;
    }

    public void setOpenStreamIterator(boolean openStreamIterator) {
        this.openStreamIterator = openStreamIterator;
    }

    public TemplateType getSqlTemplateType() {
        return sqlTemplateType;
    }

    public void setSqlTemplateType(TemplateType sqlTemplateType) {
        this.sqlTemplateType = sqlTemplateType;
    }

    public boolean isUseSystemPrint() {
        return useSystemPrint;
    }

    public void setUseSystemPrint(boolean useSystemPrint) {
        this.useSystemPrint = useSystemPrint;
    }

    public boolean isCheckEnhancement() {
        return checkEnhancement;
    }

    public void setCheckEnhancement(boolean checkEnhancement) {
        this.checkEnhancement = checkEnhancement;
    }
}
